
//
// This is the second project of CS 491
// Group member:Kusuma Bapure,Brad McGinnis,Qin Li
//

// this is the only bit that's needed to show a map:
InteractiveMap map;

//================Declare your variables here================
String fileName = "eqdata.csv";
BufferedReader reader;
MyLocation loTimeSlider;
MyLocation loMap;
MyLocation loPlayButton;
DataClass dc = null;

final int ROAD_MAP = 1;
final int SATELLITE_MAP = 2;
final int HYBRID_MAP = 3;
int timer = 0 ;
boolean isDragging = false;
boolean isTimeSliderHandle = false;
boolean isMagnitudeSliderHandle = false;
int isRadiusHighlight = -1;
int isRadiusClicked = -1;
boolean highlightFlag = false; //Flag used to show whether there are point be highlighted
boolean popupRunning = false;
boolean newCenter = false;
float[] newCoordinates;


float curCenterLat;//Map center, used to get data
float curCenterLon;
float curRadius;
color textBkColor;
color textEdge;
color textColor;
//Important: model 0 is general map, showing the points statically
//           model 1 is animation mode, points popping up automatically
int mode =0; 
final float POPUP_SPEED = 1.0;
float popupTimer = 0;
int popupCount = 0 ;
int popupEnd = 0;
PImage playButton ;
PImage stopButton;
boolean popupStart = true;
ArrayList data;
final int ARRAYLIST_SIZE =400;
boolean isFirstViewOfMode2 = true;
float transX;
float transY;

Sidebar sidebar;
TimeSlider timeSlider;
MagnitudeLegend mLegend;
RadiusLegend rLegend;
TextField textField;
QuakeList biggestQuakes;
Grapher gr = null;



//================Declare variables end======================



// buttons take x,y and width,height:
ZoomButton out; 
ZoomButton in; 
PanButton up; 
PanButton down;
PanButton left; 
PanButton right;
Button about;
Button help;

// all the buttons in one place, for looping:
Button[] buttons;


PFont font;


void setup() {
  size(1024, 700);
  smooth();

  int provider = HYBRID_MAP;

  // create a new map, optionally specify a provider
  //
  loMap = new MyLocation(200,110,width-225,height-160);
  //loMap = new MyLocation(200,125,width-225,height-175);
  Point2f dimensions = new Point2f(loMap.getWidth(), loMap.getHeight());
  Coordinate coordinate = new Coordinate(1,1,2);
  Point2f offset = new Point2f(loMap.x1,loMap.y1);
  map = new InteractiveMap(this, 
  provider == ROAD_MAP ? new Microsoft.RoadProvider() :
  provider == SATELLITE_MAP ? new Microsoft.AerialProvider() :
  new Microsoft.HybridProvider());

  //================Add variable setup here======================
  dc = new DataClass();
  dc.readData();
  gr = new Grapher(0,0,176,700);
  rLegend = new RadiusLegend();
  sidebar = new Sidebar(0, 0, 196, 700);
  timeSlider = new TimeSlider(loMap.x1,loMap.y2,loMap.getWidth(),13);
  float leftSpace = 30;
  mLegend = new MagnitudeLegend(leftSpace,(loMap.y1+loMap.y2)/2,loMap.x1-2*leftSpace,(loMap.y2-loMap.y1)/2 , false);
  curCenterLat = 51.500;
  curCenterLon = -0.126;
  curRadius = 100;
  textBkColor = color (255,255,253);
  textEdge =color(178,183,187);
  textColor = color(0,0,0);
  mode = 0 ;
  popupTimer = 0 ;
  popupCount = 0 ;
  playButton = loadImage("playButton.png");
 stopButton = loadImage("stopButton.png"); 
  loPlayButton = new MyLocation(timeSlider.x1+10, timeSlider.y1-45, 30, 30);
  data = new ArrayList();
  isFirstViewOfMode2 = true;
  textField = new TextField( 9, 236, 43, (int)mLegend.x1, (int)loMap.y1);
  biggestQuakes = new QuakeList ( 9, 236, 43, 30, 200);
  //about = new Button(loMap.x2-170,loMap.y1-80,80,25,"ABOUT");
  help = new Button(loMap.x2-170+90,loMap.y1-80,80,25,"HELP");
  gr = new Grapher(0,0,sidebar.tWidth,700);
  //println("mLegend.w = "+mLegend.w  );
  

  //================Variable setup end===========================

  //set the initial location and zoom level to London:
  map.setCenterZoom(new Location(curCenterLat, curCenterLon),2);
  //println("curCenterLat is "+curCenterLat+" curCenterLon is "+ curCenterLon);
  // zoom 0 is the whole world, 19 is street level
  // (try some out, or use getlatlon.com to search for more)

  out = new ZoomButton(5+loMap.x1,5+loMap.y1,14,14,false);
  in = new ZoomButton(22+loMap.x1,5+loMap.y1,14,14,true);
  up = new PanButton(14+loMap.x1,25+loMap.y1,14,14,UP);
  down = new PanButton(14+loMap.x1,57+loMap.y1,14,14,DOWN);
  left = new PanButton(5+loMap.x1,41+loMap.y1,14,14,LEFT);
  right = new PanButton(22+loMap.x1,41+loMap.y1,14,14,RIGHT);

  buttons = new Button[6];
  buttons[0] = in;
  buttons[1] = out;
  buttons[2] = up;
  buttons[3] = down;
  buttons[4] = left;
  buttons[5] = right;

  // set a default font for labels
  font = createFont("Helvetica", 12);

  // enable the mouse wheel, for zooming
  addMouseWheelListener(new java.awt.event.MouseWheelListener() { 
    public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { 
      mouseWheel(evt.getWheelRotation());
    }
  }
  );
}

void draw() {
  

  background(0);
  
  if(newCenter)
  {
    shiftView();
  }

    int t = millis();
  float dt = (float)(t-timer)/1000.0;
  timer = t;

  // draw the map:
  pushMatrix();
  transX =   (loMap.x1+loMap.x2)/2 - width/2;
  transY = (loMap.y1+loMap.y2)/2 -  height/2 ;
  translate(transX,transY);
  map.draw();
   switch(mode)
  {
    case 0:plotAllData();
          break;
    case 1:
    popupPoints(dt);
    break;
    case 2:
    Location center = new Location(curCenterLat, curCenterLon);
    plotAllData(center, rLegend.km2Location(center,rLegend.getRadius(isRadiusClicked)));
    break;
    default:plotAllData();
    break;
  }
  popMatrix();

  smooth();

  //================Add draw function here======================

  
  drawFrame();
  timeSlider.render();
  mLegend.render();
  textField.render();
  biggestQuakes.render();
  sidebar.render(dt);
  rLegend.render(isRadiusHighlight,isRadiusClicked);

  drawPlayButton();

  



 

  if( isTimeSliderHandle)
  {
    timeSlider.drawHandleHelp(mouseX,mouseY);
  }
  if(isMagnitudeSliderHandle)
  {
    mLegend.drawHandleHelp(mouseX,mouseY);
  }
  
  Location centerLo = map.pointLocation( width/2,height/2 );
  curCenterLat = centerLo.lat;
  curCenterLon = centerLo.lon;
  help.draw();
  //about.draw();
  String source ="Source: U.S. Geological Survey";
  fill(255,255,255);
  text(source,loMap.x2-170,loMap.y1-40,textWidth(source),30 );

  //gr.draw();
  //================Draw function end===========================



  // draw all the buttons and check for mouse-over
  boolean hand = false;
    for (int i = 0; i < buttons.length; i++) {
      buttons[i].draw();
      hand = hand || buttons[i].mouseOver();
    }


  // if we're over a button, use the finger pointer
  // otherwise use the cross
  // (I wish Java had the open/closed hand for "move" cursors)
  //cursor(hand ? HAND : CROSS);

  // see if the arrow keys or +/- keys are pressed:
  // (also check space and z, to reset or round zoom levels)
  if (keyPressed) {
    if (key == CODED) {
      if (keyCode == LEFT) {
        map.tx += 5.0/map.sc;
      }
      else if (keyCode == RIGHT) {
        map.tx -= 5.0/map.sc;
      }
      else if (keyCode == UP) {
        map.ty += 5.0/map.sc;
      }
      else if (keyCode == DOWN) {
        map.ty -= 5.0/map.sc;
      }
    }  
    else if (key == '+' || key == '=') {
      map.sc *= 1.05;
    }
    else if (key == '_' || key == '-' && map.sc > 2) {
      map.sc *= 1.0/1.05;
    }
  }


}



void keyReleased() {
   if(key == 'b' || key =='B'){
    mLegend.changeColorMode();
  }
}


void keyTyped()
{
  textField.textHandler(key);
  if ( key == ENTER || key == RETURN)
  {
    newCenter = true;
    newCoordinates = textField.getCoordinates();
  }
}


void mouseMoved()
{
  if( timeSlider.mouseMoved() )
  {
    isTimeSliderHandle = true;
    //timeSlider.drawHandleHelp(mouseX,mouseY);
  }
  else
  {
    isTimeSliderHandle = false;
  }
  //println("mouse moved");
  if( mLegend.mouseMoved() )
  {
    isMagnitudeSliderHandle = true;
  }
  else
  {
    isMagnitudeSliderHandle = false;
  }
  //to see if over play button

  isRadiusHighlight = rLegend.mouseMoved();
}

void mouseReleased()
{
  isDragging = false;
  timeSlider.mouseReleased();
  mLegend.mouseReleased();
}


// see if we're over any buttons, otherwise tell the map to drag
void mouseDragged() {
  boolean hand = false;

    for (int i = 0; i < buttons.length; i++) {
      hand = hand || buttons[i].mouseOver();
      if (hand) break;
    
  }
  //Drag map
  if ((!hand) &&( ! isDragging)&& (mouseX>=loMap.x1&&mouseX<=loMap.x2)&&(mouseY>=loMap.y1&&mouseY<=loMap.y2-timeSlider.barHeight)) {
     
    Location topLeftLo = map.pointLocation(loMap.x1,loMap.y1);
    Location bottomRightLo = map.pointLocation(loMap.x1+loMap.getWidth(),loMap.y1+loMap.getHeight()-30 );
   //println("topLeft.lat is "+topLeftLo.lat+", topLeft.lon is"+topLeftLo.lon);
 // println("bottomRightLo.lat is "+bottomRightLo.lat+", bottomRightLo.lon is"+bottomRightLo.lon);
    map.mouseDragged();
    //println("x1 is"+map.x1+",y1 is"+map.y1+",x2 is"+x2+",y2 is"+y2);
  }  

  if( mLegend.mouseDragged())
  {
    isDragging = true;
    isMagnitudeSliderHandle = false;
  }
  if( timeSlider.mouseDragged() )
  {
    isDragging = true;
    isTimeSliderHandle = false;
  }
}

// zoom in or out:
void mouseWheel(int delta) {
  if((mouseX>=loMap.x1&&mouseX<=loMap.x2)&&(mouseY>=loMap.y1&&mouseY<=loMap.y2))
  {
    if (delta > 0) {
      map.sc *= 1.05;
    }
    else if (delta < 0) {
      map.sc *= 1.0/1.05;
    }
  }
}

// see if we're over any buttons, and respond accordingly:
void mouseClicked() {
  if (in.mouseOver()) {
    map.zoomIn();
  }
  else if (out.mouseOver()) {
    map.zoomOut();
  }
  else if (up.mouseOver()) {
    map.panUp();
  }
  else if (down.mouseOver()) {
    map.panDown();
  }
  else if (left.mouseOver()) {
    map.panLeft();
  }
  else if (right.mouseOver()) {
    map.panRight();
  }
  /*else if ( about.mouseOver()){
    this.showMessageDialog ();
  }*/
  else if ( help.mouseOver()){
    //println("clicked");
    link("http://code.google.com/p/cs491project2/");
  }
  if( mouseX>=loPlayButton.x1 && mouseX< loPlayButton.x1+loPlayButton.getWidth() && mouseY>=loPlayButton.y1 &&mouseY<=loPlayButton.y1+loPlayButton.getHeight() )
    {
      if(!popupRunning)
      {
        mode = 1;
        popupStart = true;
        popupRunning = true;
      }
      else //end mode 1, switch to previous mode
      {
        if( isRadiusClicked >=0)
        mode = 2;
        else
        mode = 0 ;
        popupStart = false;
        popupRunning = false;
      }
    }
  sidebar.mouseClicked();
  timeSlider.mouseClicked();
  isRadiusClicked = rLegend.mouseClicked();
  println("isRadiusClicked = "+isRadiusClicked);
  if( mouseButton == LEFT)
  {
    textField.mouseButtonHandler( mouseX, mouseY);
    
    if( !textField.isBlocking( biggestQuakes.getX(), biggestQuakes.getY()))
    {
      biggestQuakes.mouseButtonHandler( mouseX, mouseY);
      if( biggestQuakes.hasSelection())
      {
        newCenter = true;
        newCoordinates = biggestQuakes.getCoordinates();
      }
    }
  }
}

void mousePressed()
{
  //println("mouse is pressed");
  timeSlider.mousePressed();
  mLegend.mousePressed();
}

void plotData(float x, float y,float m)
{
  //Location location = new Location(x,y);
  //Point2f p = map.locationPoint(location);
  noStroke();
  ellipseMode(CENTER);

  //i is levels identified by magnitudes
  int i = mLegend.getColorIndex(m);
  color c = mLegend.getColor(i);
  fill(c,100);
  stroke(c);
  strokeWeight(3);
  ellipse(x, y, 10+6*(i+1), 10+6*(i+1));
  fill(c);
  noStroke();
  if( i<2)
    ellipse(x, y, 6, 6);
  else
    ellipse(x, y, 8, 8);

  //fill(colorPoints[i],100);
  //ellipse(p.x+i*50, p.y, 5+5*(i+1), 5+5*(i+1));
}

void plotAllData() {
  
  highlightFlag =false;
  Location topLeftLo = map.pointLocation(0,0);
  Location bottomRightLo = map.pointLocation(loMap.x1+loMap.getWidth(),loMap.y1+loMap.getHeight()-30 );
  //Location centerLo = map.pointLocation( (loMap.x1+loMap.getWidth())/2,(loMap.y1+loMap.getHeight())/2 );

  //println("curCenterLat is "+curCenterLat+", curCenterLon is "+curCenterLon);
  //map.setCenterZoom(new Location(curCenterLat, curCenterLon),2);
    
  // Caution:The lattitude of South semisphere is negative , North semisphere is positive
  //         The longitude of West semisphere is negative , East semisphere is positive
  Date begin = timeSlider.getLeftDate();
  Date end = timeSlider.getRightDate();
  float top = mLegend.getTopMagnitude();
  float bottom = mLegend.getBottomMagnitude();
  
  data.clear();
  //println("topLeft.lat is "+topLeftLo.lat+", topLeft.lon is"+topLeftLo.lon);
  //println("bottomRightLo.lat is "+bottomRightLo.lat+", bottomRightLo.lon is"+bottomRightLo.lon);
  
  data = dc.readData( topLeftLo.lat, topLeftLo.lon, bottomRightLo.lat,bottomRightLo.lon ,top,bottom,begin,end  );
  // println("datasize in plotalldata is "+data.size());
  DataRow dr;
  float tempX=0;
  float tempY = 0 ;
  DataRow tempDr = new DataRow();
  
  int num = data.size()>ARRAYLIST_SIZE?ARRAYLIST_SIZE:data.size();
  for( int i = 0; i < num; i ++) {
    dr = (DataRow) data.get(i);
    Point2f p = map.locationPoint(dr.getLocation());
    fill(100,100,100);
    stroke(255, 255, 255);
    //ellipse(p.x, p.y, 10, 10);
    plotData(p.x,p.y,dr.getMagnitude() );
      if(highlightFlag==false &&dist(mouseX,mouseY, p.x+transX,p.y+transY)<3)
      {
        highlightFlag =true;
        tempX =p.x;
        tempY = p.y;
        tempDr = dr;        
      }
  }
  if( highlightFlag )
  drawPointText(tempX,tempY, tempDr);
  
}

void plotAllData( Location center, Location [] location ) {
  
  //println("center.lat = "+center.lat + ",center.lon = "+center.lon);
  //println("location[0].lat = "+location[0].lat + ",location[0].lon = "+location[0].lon);
  //println("location[1].lat = "+location[1].lat + ",location[1].lon = "+location[1].lon);
  highlightFlag =false;
  if( isFirstViewOfMode2)
  {
    int zoom;
    if( rLegend.selected ==0)
      zoom = 8;
     else if ( rLegend.selected ==1)
     zoom = 7;
     else
     zoom = 6;
    map.setCenterZoom(center,zoom);  
    isFirstViewOfMode2 = false;
  }
  
   
  // Caution:The lattitude of South semisphere is negative , North semisphere is positive
  //         The longitude of West semisphere is negative , East semisphere is positive
  Date begin = timeSlider.getLeftDate();
  Date end = timeSlider.getRightDate();
  float top = mLegend.getTopMagnitude();
  float bottom = mLegend.getBottomMagnitude();
  Location topLeftLo = map.pointLocation(loMap.x1,loMap.y1);
  Location bottomRightLo = map.pointLocation(loMap.x1+loMap.getWidth(),loMap.y1+loMap.getHeight()-30 );
  float lat1 = location[0].lat<topLeftLo.lat? location[0].lat:topLeftLo.lat;
  float lat2 = location[1].lat>bottomRightLo.lat? location[1].lat : bottomRightLo.lat;
  float lon1 = location[0].lon> topLeftLo.lon? location[0].lon:topLeftLo.lon;
  float lon2 = location[1].lon< bottomRightLo.lon?location[1].lon:bottomRightLo.lon;
  
  data.clear();
  data = dc.readData( lat1,lon1,lat2,lon2 ,top,bottom,begin,end  );
  //println("datasize in plotalldata(1,2) is "+data.size());
  DataRow dr;
  float tempX=0;
  float tempY = 0 ;
  DataRow tempDr = new DataRow();
  
  int num = data.size()>ARRAYLIST_SIZE?ARRAYLIST_SIZE:data.size();
  for( int i = 0; i < num; i ++) {
    dr = (DataRow) data.get(i);
    Point2f p = map.locationPoint(dr.getLocation());
    fill(100,100,100);
    stroke(255, 255, 255);
    //ellipse(p.x, p.y, 10, 10);
    plotData(p.x,p.y,dr.getMagnitude() );
      if(highlightFlag==false &&dist(mouseX,mouseY, p.x,p.y)<3)
      {
        highlightFlag =true;
        tempX =p.x;
        tempY = p.y;
        tempDr = dr;        
      }
  }
  if( highlightFlag )
  drawPointText(tempX,tempY, tempDr);
  
}

void popupPoints( float dt)
{ 
  if( popupStart ) //reset popupCount to left
  {
    //println("left is "+timeSlider.left+", right is "+timeSlider.right);
    popupCount =int(timeSlider.left);
    popupEnd = int(timeSlider.right);
    
    popupTimer =popupCount;
    //println("This is "+popupCount);
    timeSlider.setLeftDate(popupCount);
    timeSlider.setRightDate(popupCount+1);
    popupCount++;
    popupStart = false;
  }
 
  popupTimer +=dt;
  for( int i = popupCount;i<popupEnd;i++)
  {
    if( abs( popupTimer - i*POPUP_SPEED )<=0.05  )
    {      
      
      timeSlider.setLeftDate(i);
      timeSlider.setRightDate(i+1);      
      popupCount++;
      //println("This is "+i+", popupCount is "+popupCount);      
    }
  }
  if( popupCount == popupEnd) //reach the end
  {
    popupRunning=false;
  }
  plotAllData();
}
void drawPlayButton()
{
  if( !popupRunning )
    image(playButton,loPlayButton.x1,loPlayButton.y1,loPlayButton.getWidth(),loPlayButton.getHeight() );
    else 
    image(stopButton,loPlayButton.x1,loPlayButton.y1,loPlayButton.getWidth(),loPlayButton.getHeight() );
  if( mouseX>=loPlayButton.x1&& mouseX< loPlayButton.x1+loPlayButton.getWidth() && mouseY>=loPlayButton.y1 &&mouseY<=loPlayButton.y1+loPlayButton.getHeight() )
  {
    float width= 150;
    float height = 40;
    stroke(178,183,187);
    strokeWeight(1);
    fill(255,255,253);
    rect(mouseX-width/2,mouseY-height-5,width,height);
    fill(0);
    textSize(10);
    textAlign(CENTER,CENTER);
    text("Click the play button to see earthquake occurrence",mouseX-width/2,mouseY-height-5,width,height);
    cursor(HAND);
  }
}

void drawPointText(float x, float y, DataRow dr)
{
  //println("This is a text");
  int tWidth = 120;
  int tHeight =65;
  stroke(textEdge);
  strokeWeight(1);
  fill(textBkColor);
  rectMode(CORNER);
  rect( x,y-tHeight,tWidth,tHeight);
  fill(textColor);
  textSize(10);
  textAlign( LEFT,TOP);
  
  float lat = (float) dr.getLatitude() ;
  float lon = (float) dr.getLongitude();
  DecimalFormat format = new DecimalFormat("#.0");
  String latString =format.format(  abs( lat)  ) ;
  String lonString = format.format( abs (lon) );
  if( lat>0)
    latString +=" N"; 
   else if( lat<0)
   latString += " S";
  if( lon>0)
    lonString +=" E";
   else if ( lon<0)
     lonString +=" W";
  String te ="Time:"+ dr.getYear()+ "-" + dr.getMonth() +"-"+ dr.getDay()+
  "\nPosition:"+ latString +" ," +lonString +
  "\nMagnitude:"+ dr.getMagnitude()+
  "\nDepth:"+ dr.getDepth();
  
  text( te ,x+2,y-tHeight,tWidth,tHeight);
  textAlign(LEFT);
}
void drawFrame()
{
  //fill(180, 180, 255, 80);
  fill(#000045,250);
  noStroke();
  rect(0,0,loMap.x1,height); //Left part
  rect(loMap.x2,0,width-loMap.x2,height);  //Right part
  rect(loMap.x1,0,loMap.getWidth(),loMap.y1); //Left Top part
  rect(loMap.x1,loMap.y2,loMap.getWidth(),height-loMap.y2); //Left Bottom part
  fill(#19D9BE,0);
  stroke(#33CC99);
  strokeWeight(2);
  rect(loMap.x1,loMap.y1,loMap.getWidth(),loMap.getHeight());
}


//moves the center of the map to the new coordinates given
void shiftView()
{
  if( newCoordinates[0] <= 87.0 && newCoordinates[1] <= 180.0)
  {
    Location oldPosition = map.pointLocation( width/2, height/2);
    if( (abs(newCoordinates[0] - oldPosition.lat) > 0.01) && (abs(newCoordinates[1] - oldPosition.lon) > 0.01))
    {
      float latitude = oldPosition.lat + ( (newCoordinates[0] - oldPosition.lat) * (1/frameRate));
      float longitude = oldPosition.lon + ( (newCoordinates[1] - oldPosition.lon) * (1/frameRate));
      map.setCenter( new Location( latitude, longitude));
    }
    else
    {
      newCenter = false;
    }
  }
  else
  {
    newCenter = false;
  }
}

